package genetic.q3;

import java.util.LinkedList;
import java.util.List;

import genetic.Function;
import genetic.Genotype;
import genetic.Phenotype;
import genetic.Terminal;
import genetic.TreeNode;
import genetic.q3.functions.Constant;
import genetic.q3.functions.Divide;
import genetic.q3.functions.Minus;
import genetic.q3.functions.Mult;
import genetic.q3.functions.Plus;
import genetic.q3.functions.X;
import genetic.q3.functions.math.IEEEremainder;
import genetic.q3.functions.math.abs;
import genetic.q3.functions.math.acos;
import genetic.q3.functions.math.asin;
import genetic.q3.functions.math.atan;
import genetic.q3.functions.math.atan2;
import genetic.q3.functions.math.cbrt;
import genetic.q3.functions.math.ceil;
//import genetic.q3.functions.math.copySign;
import genetic.q3.functions.math.cos;
import genetic.q3.functions.math.cosh;
import genetic.q3.functions.math.exp;
import genetic.q3.functions.math.expm1;
import genetic.q3.functions.math.floor;
//import genetic.q3.functions.math.getExponent;
import genetic.q3.functions.math.hypot;
import genetic.q3.functions.math.log;
import genetic.q3.functions.math.log10;
import genetic.q3.functions.math.log1p;
import genetic.q3.functions.math.max;
import genetic.q3.functions.math.min;
//import genetic.q3.functions.math.nextAfter;
//import genetic.q3.functions.math.nextUp;
import genetic.q3.functions.math.pow;
import genetic.q3.functions.math.rint;
import genetic.q3.functions.math.round;
//import genetic.q3.functions.math.scalb;
import genetic.q3.functions.math.signum;
import genetic.q3.functions.math.sin;
import genetic.q3.functions.math.sinh;
import genetic.q3.functions.math.sqrt;
import genetic.q3.functions.math.tan;
import genetic.q3.functions.math.tanh;
import genetic.q3.functions.math.toDegrees;
import genetic.q3.functions.math.toRadians;
import genetic.q3.functions.math.ulp;

public class SymbolicRegressionPhenotype extends Phenotype {

	private static final int INIT_DMAX = 4;
	private static final int MUTATE_DMAX = 3;

	public double[][] points;

	public SymbolicRegressionPhenotype(double[][] points, int type) {
		this.points = points;
		List<Function> functions = new LinkedList<Function>();
		List<Terminal> terminals = new LinkedList<Terminal>();
		terminals.add(new X());
		terminals.add(new Constant(1));
		terminals.add(new Constant(2));
		terminals.add(new Constant(3));
		terminals.add(new Constant(4));
		terminals.add(new Constant(5));
		functions.add(new Minus());
		functions.add(new Mult());
		functions.add(new Plus());
		if (type == 0){
			functions.add(new Divide());
			functions.add(new abs());
			functions.add(new acos());
			functions.add(new asin());
			functions.add(new atan());
			functions.add(new atan2());
			functions.add(new cbrt());
			functions.add(new ceil());
			//functions.add(new copySign());
			functions.add(new cos());
			functions.add(new cosh());
			functions.add(new exp());
			functions.add(new expm1());
			functions.add(new floor());
			//functions.add(new getExponent());
			functions.add(new hypot());
			functions.add(new IEEEremainder());
			functions.add(new log());
			functions.add(new log10());
			functions.add(new log1p());
			functions.add(new max());
			functions.add(new min());
			//functions.add(new nextAfter());
			//functions.add(new nextUp());
			functions.add(new pow());
			functions.add(new rint());
			functions.add(new round());
			//functions.add(new scalb());
			functions.add(new signum());
			functions.add(new sin());
			functions.add(new sinh());
			functions.add(new sqrt());
			functions.add(new tan());
			functions.add(new tanh());
			functions.add(new toDegrees());
			functions.add(new toRadians());
			functions.add(new ulp());
		} else if (type == 1){
			functions.add(new Divide());
			functions.add(new pow());
			functions.add(new exp());
			functions.add(new log());
			functions.add(new round());
		} else if (type == 2){
			functions.add(new Divide());
			functions.add(new abs());
			functions.add(new max());
			functions.add(new min());
			functions.add(new sqrt());
			functions.add(new tan());
		} else if (type == 3){
			functions.add(new abs());
			functions.add(new ceil());
			functions.add(new log());
			functions.add(new cos());
			functions.add(new pow());
		}
		init(functions, terminals);
	}

	@Override
	public Genotype[] crossover(Genotype g1, Genotype g2) {
		return crossoverDefault(g1, g2, 2);
	}

	@Override
	public void doEvery50(List<Genotype> population) {
	}

	@Override
	public double fitness(Genotype g) {
		return ((SymbolicRegressionGenotype)g).getFitness();
	}

	@Override
	public List<Genotype> getInitialPopulation(int populationSize) {
		return getInitialPopulationCommon(populationSize, INIT_DMAX);
	}

	@Override
	public Genotype makeInstanceFromTree(TreeNode tree) {
		return new SymbolicRegressionGenotype(tree);
	}

	@Override
	public Genotype mutate(Genotype g) {
		return mutateDefault(g, MUTATE_DMAX);
	}

	@Override
	public Genotype[] selectParents(List<Genotype> population, int populationSize) {
//		return selectMatingParentsRouletteWheel(population, populationSize);
		return selectMatingPoolTournamentSelection(population, populationSize, 6, 2, 0.4);
//		return selectMatingPoolTournamentSelection4(population, populationSize, 20);
	}

	@Override
	public void updateFitness(List<Genotype> population) {
		for (Genotype g: population){
			((SymbolicRegressionGenotype)g).computeFitness(points);
//			System.out.println(g);
		}
	}
	
}
